package com.hevs.samplewebapp.client.screens;

import java.util.Comparator;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.SimplePager;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.SimplePager.Resources;
import com.google.gwt.user.cellview.client.SimplePager.TextLocation;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.hevs.samplewebapp.client.bean.CompositeWRefresh;
import com.hevs.samplewebapp.client.dialogbox.ConfirmDialogBox;
import com.hevs.samplewebapp.client.bean.User;
import com.hevs.samplewebapp.client.services.UserAdministrationServiceImpl;
import com.hevs.samplewebapp.shared.security.CredentialLevel;

/**
 * This is the Users Management Tab
 * <p>The Goal of this tab is to give the administrator the tools to fully manage 
 * the users of the application.</p> 
 * @author Thomas Rouvinez
 * @Date: 26.03.2012
 */
public class ManageUsersWidget extends CompositeWRefresh {

	// ------------------------------------------------------------
	// Variables
	// ------------------------------------------------------------

	/**
	 * Horizontal panel contains the widget
	 */
	private HorizontalPanel mainPanel =  new HorizontalPanel();

	/**
	 * Vertical panel contains the form to display and update a user
	 */
	private VerticalPanel vPanelAdmin =  new VerticalPanel();

	/**
	 * Flextable with the different button for management
	 */
	private FlexTable hPanel =  new FlexTable();

	/**
	 * Textbox with the firstname of the selected user
	 */
	private TextBox firstName = new TextBox();

	/**
	 * Textbox with the lastname of the selected user
	 */
	private TextBox lastName = new TextBox();

	/**
	 * Textbox with the email of the selected user
	 */
	private TextBox email = new TextBox();

	/**
	 * Textbox with the login of the selected user
	 */
	private TextBox login = new TextBox();

	/**
	 * List box with the credential level of the selected user
	 */
	private ListBox credentialLevel = new ListBox();

	/**
	 * Button to create a new user
	 */
	private Button create = new Button("Add");

	/**
	 * Button to delete a selected user
	 */
	private Button delete = new Button("Delete");

	/**
	 * Button to update a selected user
	 */
	private Button update = new Button("Update");

	/**
	 * Button to reset the password of a selected user
	 */
	private Button resetPass = new Button("Reset Password");

	/**
	 * List of user for the Celltable
	 */
	private ListDataProvider<User> userDataProvider;

	/**
	 * Celltable with the list of user in it
	 */
	private static CellTable<User> usersTable;

	/**
	 * Column with users firstname
	 */
	private TextColumn<User> firstNameColumn;

	/**
	 * Column with users lastname
	 */
	private TextColumn<User> lastNameColumn;

	/**
	 * Column with users credential level
	 */
	private TextColumn<User> credentialColumn;

	/**
	 * Pager to go trought the list of users when they are above 15
	 */
	private SimplePager usersPager;

	/**
	 * Handler to sort users by firstname
	 */
	private ListHandler<User> firstNameSortHandler;

	/**
	 * Handler to sort users by lastname
	 */
	private ListHandler<User> lastNameSortHandler;

	/**
	 * Handler to sort users by credential level
	 */
	private ListHandler<User> credentialSortHandler;

	/**
	 * A simple selection model that allows only one object to be selected a a time.
	 */
	private SingleSelectionModel<User> selectionModel;

	/**
	 * ClientBundle that provides pager images for this widget   
	 */
	private Resources resources=GWT.create(SimplePager.Resources.class); 

	/**
	 * Selected user
	 */
	private User currentEditedUser;

	/**
	 * Users list from the web service
	 */
	private static List<User> userListRest;

	/**
	 * List of users
	 */
	private List<User> userList;

	/**
	 * Initialization of the administrator web services
	 */
	private static UserAdministrationServiceImpl userService = new UserAdministrationServiceImpl();

	// ------------------------------------------------------------------------
	// CONSTRUCTOR
	// ------------------------------------------------------------------------

	/**
	 * Constructor of the manage user widget
	 */
	public ManageUsersWidget(){
		// ------------------------------------------------------------------------
		// Layout description.
		// ------------------------------------------------------------------------

		mainPanel.setSpacing(8);
		mainPanel.add(CreateUserList());
		mainPanel.add(userForm());

		setButtonListeners();

		// ------------------------------------------------------------------------
		// Widget initialization.
		// ------------------------------------------------------------------------
		RefreshCurrentCellTable();
		initWidget(mainPanel);
	}

	// ----------------------------------------------------------------------------
	// Methods.
	// ----------------------------------------------------------------------------

	/**
	 * Method to create the user details form.
	 * @return
	 * the widget with the fields to display the information of the selected user
	 */
	private Widget userForm(){
		// Set a flexTable.
		FlexTable layout = new FlexTable();
		layout.setCellSpacing(6);
		FlexCellFormatter cellFormatter = layout.getFlexCellFormatter();

		// Define types of authorization level.
		for(int i=0;i<CredentialLevel.getSize();i++)
		{
			CredentialLevel temp=CredentialLevel.get(i);
			credentialLevel.addItem(temp.getName(),String.valueOf(temp.getVal()));
		}

		// Add a title to the form.
		layout.setHTML(0, 0, "User details");
		cellFormatter.setColSpan(0, 0, 2);
		cellFormatter.setHorizontalAlignment(0, 0, HasHorizontalAlignment.ALIGN_CENTER);

		// Fill the form in the flexTable.
		layout.setHTML(1, 0, "First name:");
		layout.setWidget(1, 1, firstName);

		layout.setHTML(2, 0, "Last name:");
		layout.setWidget(2, 1, lastName);

		layout.setHTML(3, 0, "Login:");
		layout.setWidget(3, 1, login);

		layout.setHTML(4, 0, "Email:");
		layout.setWidget(4, 1, email);

		layout.setHTML(5, 0, "Credential:");
		layout.setWidget(5, 1, credentialLevel);

		// Prepare buttons for administration options.
		hPanel.setWidget(0, 1, create);
		hPanel.setWidget(0, 2, update);
		hPanel.setWidget(0, 3, resetPass);
		hPanel.setWidget(0, 4, delete);

		create.setWidth("100%");
		update.setWidth("100%");
		resetPass.setWidth("100%");
		delete.setWidth("100%");

		hPanel.setWidth("100%");

		// Wrap the content in a DecoratorPanel.
		vPanelAdmin.add(layout);
		vPanelAdmin.add(hPanel);

		DecoratorPanel decPanel = new DecoratorPanel();
		decPanel.setWidget(vPanelAdmin);

		return decPanel;
	}

	/**
	 * Method to create the cell table.
	 * @return
	 * the widget wit the list of the users in it
	 */
	private Widget CreateUserList(){

		VerticalPanel vPanel=new VerticalPanel();

		// Cell table with users' names.
		usersTable = new CellTable<User>();

		// Get the list of doctors for the current user into the table content provider.
		userDataProvider = new ListDataProvider<User>();

		// Create the pager for the table
		usersPager=new SimplePager(TextLocation.CENTER,resources,false,1000,true);
		usersPager.setDisplay(usersTable);
		usersPager.setPageSize(10);

		// Create the first name header of column.
		firstNameColumn = new TextColumn<User>() {
			@Override
			public String getValue(User user) {
				return user.getFirstName();
			}
		};

		// Create the last name header of column.
		lastNameColumn = new TextColumn<User>() {
			@Override
			public String getValue(User user) {
				return user.getLastName();
			}
		};

		// Create the credential header of column
		credentialColumn=new TextColumn<User>(){
			@Override
			public String getValue(User user)
			{
				return CredentialLevel.get(user.getCredentialLevel()).getName();
			}
		};

		// Make the relevant columns sortable (all in this case).
		firstNameColumn.setSortable(true);
		lastNameColumn.setSortable(true);
		credentialColumn.setSortable(true);

		// Add the columns to the cell table.
		usersTable.addColumn(firstNameColumn, "First name");
		usersTable.addColumn(lastNameColumn, "Last name");
		usersTable.addColumn(credentialColumn, "Credential");

		// Set columns sizes.
		usersTable.setColumnWidth(firstNameColumn, 25.0, Unit.PCT);
		usersTable.setColumnWidth(lastNameColumn, 25.0, Unit.PCT);
		usersTable.setColumnWidth(credentialColumn,25.0,Unit.PCT);

		// Set content of the table from the list of granted doctors for the current user.
		userDataProvider.addDataDisplay(usersTable);

		vPanel.add(usersTable);
		vPanel.add(usersPager);
		vPanel.setCellHorizontalAlignment(usersPager,VerticalPanel.ALIGN_CENTER);


		getData();

		RefreshCurrentCellTable();

		// Return the newly created users' table.
		return vPanel;
	}

	/**
	 * Method to get the users list from server.
	 * @return
	 * list of the application users
	 */
	private List<User> GetUserList(){
		// Ask service to make the call to the server.
		userService.getUsers(ManageUsersWidget.this);

		// Get the response list.
		return userService.getUserSet();
	}

	/**
	 * Method to set the List of user
	 */
	public void getData(){
		userListRest = GetUserList();
	}

	/**
	 * Method to refresh the cell table.
	 */
	public void RefreshCurrentCellTable(){
		// Rest call to get a list of doctors for a given user.
		usersTable.setRowCount(0);
		getData();

		// Set the new list as content of the users' table.
		CellTableInnerListeners();
		usersTable.setRowCount(userListRest.size(), true);

		// Reset current user.
		currentEditedUser = null;		
	}

	@Override
	/**
	 * Method to refresh component
	 */
	public void refreshComp() {
		userListRest.clear();

		userListRest=userService.getUserSet();

		userList=userDataProvider.getList();
		userList.clear();

		for (User users : userListRest) {
			userList.add(users);
		}
		
		usersTable.setRowCount(userListRest.size(), true);

		// Reset current user.
		currentEditedUser = null;
	}

	/**
	 * Common method to update the cell table completely.
	 */
	private void CellTableInnerListeners() {
		// Add the data to the data provider (filling the GUI table).
		userList = userDataProvider.getList();

		// Populate current list of content.
		for (User users : userListRest) {
			userList.add(users);
		}

		// Cell table's columns sorters.
		// First name sorter listener.
		firstNameSortHandler = new ListHandler<User>(userList);
		firstNameSortHandler.setComparator(firstNameColumn,
				new Comparator<User>() {
			public int compare(User o1, User o2) {
				if (o1 == o2) {
					return 0;
				}

				// Compare the name columns.
				if (o1 != null) {
					return (o2 != null) ? o1.getFirstName().compareTo(o2.getFirstName()) : 1;
				}
				return -1;
			}
		});

		// Last name sorter listener.
		lastNameSortHandler = new ListHandler<User>(userList);
		lastNameSortHandler.setComparator(lastNameColumn,
				new Comparator<User>() {
			public int compare(User o1, User o2) {
				if (o1 == o2) {
					return 0;
				}

				// Compare the name columns.
				if (o1 != null) {
					return (o2 != null) ? o1.getLastName().compareTo(o2.getLastName()) : 1;
				}
				return -1;
			}
		});

		// Credential level sorter listener.
		credentialSortHandler=new ListHandler<User>(userList);
		credentialSortHandler.setComparator(credentialColumn,
				new Comparator<User>()
				{
			public int compare(User o1,User o2)
			{
				if(o1==o2)
					return 0;

				// Compare the name columns.
				if(o1!=null)
					return (o2!=null)?CredentialLevel.get(o1.getCredentialLevel()).getName()
							.compareTo(CredentialLevel.get(o2.getCredentialLevel()).getName()):1;
							return -1;
			}
				});

		// Add a selection model to handle user selection.
		selectionModel = new SingleSelectionModel<User>();
		usersTable.setSelectionModel(selectionModel);
		selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {

			// Selection handler for rows in the table.
			public void onSelectionChange(SelectionChangeEvent event) {
				User selected = selectionModel.getSelectedObject();
				if (selected != null) {
					// Set right information in box.
					currentEditedUser = selected;

					firstName.setText(currentEditedUser.getFirstName());
					lastName.setText(currentEditedUser.getLastName());
					email.setText(currentEditedUser.getEmail());
					login.setText(currentEditedUser.getLogin());

					credentialLevel.setSelectedIndex(currentEditedUser.getCredentialLevel());
				}
			}
		});

		// Set the handlers to the columns.
		usersTable.addColumnSortHandler(firstNameSortHandler);
		usersTable.addColumnSortHandler(lastNameSortHandler);
		usersTable.addColumnSortHandler(credentialSortHandler);

		// We know that the data is sorted alphabetically by default.
		usersTable.getColumnSortList().push(firstNameColumn);
		usersTable.getColumnSortList().push(lastNameColumn);
		usersTable.getColumnSortList().push(credentialColumn);
	}


	// ----------------------------------------------------------------------------
	// Methods for list refresh without new data from server.
	// ----------------------------------------------------------------------------

	/**
	 * Pop up to create a new user.
	 */
	private void askingCreateUser(){
		@SuppressWarnings("unused")
		AddNewUserWidget newUserWidget = new AddNewUserWidget(userService, this);
	}

	/**
	 * Pop up to confirm delete in cell table.
	 * @param user
	 * the user to delete
	 */
	private void askingDeleteUser(final User user){
		if(currentEditedUser!=null){
			String title = "Deleting a user";
			String[] messages = { "You are about to delete user " + user.getFirstName() + " "
					+ user.getLastName() + ", are you sure?" };

			final ConfirmDialogBox box = new ConfirmDialogBox(title, messages);

			box.show();
			box.center();
			box.addCloseHandler(new CloseHandler<PopupPanel>() {
				@Override
				public void onClose(CloseEvent<PopupPanel> event) {
					if(box.getChoice()==ConfirmDialogBox.YES){
						try {
							// Send call to server to delete user.
							userService.delete(convertUserToJS(user));

							// get the new data and refresh the table
							getData();
						} catch (Exception e) {
							Window.alert("Failed to delete current user");
						}
					}
				}
			});
		}else
			Window.alert("Please select a User");
	}

	/**
	 * Pop up to confirm password reset.
	 * @param user
	 * the user to retrieve password
	 */
	private void askingResetPassword(final User user){

		if(currentEditedUser!=null){

			String title = "Reset password";
			String[] messages = { "You are about to reset the password for  " + user.getFirstName() + " "
					+ user.getLastName() + ", are you sure?" };

			final ConfirmDialogBox box = new ConfirmDialogBox(title, messages);

			box.show();
			box.center();
			box.addCloseHandler(new CloseHandler<PopupPanel>() {
				@Override
				public void onClose(CloseEvent<PopupPanel> event) {
					if(box.getChoice()==ConfirmDialogBox.YES){
						try {
							// Send call to server to reset password.
							userService.resetPassword(convertUserToJS(user));
						} catch (Exception e) {
							Window.alert("Failed to change password");
						}
					}
				}
			});
		}else
			Window.alert("Please select a User");
	}

	/**
	 * Pop up to confirm the user's update.
	 * @param user
	 * the user to update
	 */
	private void askingUserUpdate(final User user){
		if(currentEditedUser!=null){
			String title = "Update user";
			String[] messages = { "You are about to update user  " + user.getFirstName() + " "
					+ user.getLastName() + ", are you sure?" };

			final ConfirmDialogBox box = new ConfirmDialogBox(title, messages);

			box.show();
			box.center();
			box.addCloseHandler(new CloseHandler<PopupPanel>() {
				@Override
				public void onClose(CloseEvent<PopupPanel> event) {
					if(box.getChoice()==ConfirmDialogBox.YES){
						try {
							// Send call to server to reset password.
							JSONObject updateUser = new JSONObject();
							updateUser.put("key", new JSONString(user.getKey()));
							updateUser.put("firstName", new JSONString(getFirstName().getText()));
							updateUser.put("lastName", new JSONString(getLastName().getText()));
							updateUser.put("login", new JSONString(getLogin().getText()));
							updateUser.put("email", new JSONString(getEmail().getText()));
							updateUser.put("credentialLevel", new JSONString("" + getCredentialLevel().getSelectedIndex()));

							userService.update(updateUser);

							// get new Data and refresh the table
							getData();
						} catch (Exception e) {
							Window.alert("Failed to update the user");
						}
					}
				}
			});
		}else
			Window.alert("Please select a User");
	}

	/**
	 * Convert User object to JSON.
	 * @param user
	 * the user to send to the server
	 * @return
	 * the User in JSON format
	 */
	private JSONObject convertUserToJS(User user){
		JSONObject userJson = new JSONObject();

		try {
			userJson.put("key", new JSONString(user.getKey()));
			userJson.put("firstName", new JSONString(user.getFirstName()));
			userJson.put("lastName", new JSONString(user.getLastName()));
			userJson.put("login", new JSONString(user.getLogin()));
			userJson.put("email", new JSONString(user.getEmail()));
			userJson.put("credentialLevel", new JSONString("" + user.getCredentialLevel()));
		} catch (JSONException e) {
			e.printStackTrace();
		}	

		return userJson;
	}

	// ------------------------------------------------------------------------
	// Listeners methods.
	// ------------------------------------------------------------------------

	/**
	 * Method to set the listeners on the right buttons.
	 */
	private void setButtonListeners(){
		create.addClickHandler(new createHandler());
		delete.addClickHandler(new deleteHandler());
		update.addClickHandler(new updateHandler());
		resetPass.addClickHandler(new resetPassHandler());
	}

	/**
	 * ClickHandler for the button reset password
	 *
	 */
	private class createHandler implements ClickHandler {
		@Override
		public void onClick(ClickEvent event) {
			// Create a new user.
			askingCreateUser();
		}
	}

	/**
	 * ClickHandler for the button delete.
	 */
	private class deleteHandler implements ClickHandler {
		@Override
		public void onClick(ClickEvent event) {
			// Delete user.
			askingDeleteUser(currentEditedUser);
		}
	}

	/**
	 * ClickHandler for the button update.
	 */
	private class updateHandler implements ClickHandler {
		@Override
		public void onClick(ClickEvent event) {
			// Update currently selected user.
			askingUserUpdate(currentEditedUser);
		}
	}

	/**
	 * ClickHandler for the button reset password.
	 */
	private class resetPassHandler implements ClickHandler {
		@Override
		public void onClick(ClickEvent event) {
			// Reset password for currently selected user.
			askingResetPassword(currentEditedUser);
		}
	}

	// ------------------------------------------------------------------------
	// Getters - Setters.
	// ------------------------------------------------------------------------

	/**
	 * Getter for the firstname Textbox
	 * @return
	 * the value in the firstname Textbox
	 */
	public TextBox getFirstName() {
		return firstName;
	}

	/**
	 * Setter for the firstname Textbox
	 * @param firstName
	 * the value to display in the firstname Textbox
	 */
	public void setFirstName(TextBox firstName) {
		this.firstName = firstName;
	}

	/**
	 * Getter for the lastname Textbox
	 * @return
	 * the value in the lastname Textbox
	 */
	public TextBox getLastName() {
		return lastName;
	}

	/**
	 * Setter for the lastname Textbox
	 * @param lastName
	 * the value to display in the lastname Textbox
	 */
	public void setLastName(TextBox lastName) {
		this.lastName = lastName;
	}

	/**
	 * Getter for the email Textbox
	 * @return
	 * the value in the email Textbox
	 */
	public TextBox getEmail() {
		return email;
	}

	/**
	 * Setter for the email Textbox
	 * @param email
	 * the value to display in the email Textbox
	 */
	public void setEmail(TextBox email) {
		this.email = email;
	}

	/**
	 * Getter for the login Textbox
	 * @return
	 * the value in the login Textbox
	 */
	public TextBox getLogin() {
		return login;
	}

	/**
	 * Setter for the login Textbox
	 * @param login
	 * The value to display in the login Textbox
	 */
	public void setLogin(TextBox login) {
		this.login = login;
	}

	/**
	 * Getter for the Credentiallevel Listbox
	 * @return
	 * the selected value in the credentiallevel Listbox
	 */
	public ListBox getCredentialLevel() {
		return credentialLevel;
	}

	/**
	 * Setter for the credentiallevel Listbox
	 * @param credentialLevel
	 * the value to select in the credentiallevel Listbox
	 */
	public void setCredentialLevel(ListBox credentialLevel) {
		this.credentialLevel = credentialLevel;
	}
}
